package net.qiqbframework.spring.accessing;

import lombok.extern.slf4j.Slf4j;
import org.springframework.cglib.proxy.InvocationHandler;

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.concurrent.atomic.AtomicReference;

@Slf4j
public class BlockClientProxyHandler<T> implements InvocationHandler {

    private final Class<T> type;


    private final AtomicReference<T> target;

    public BlockClientProxyHandler(Class<T> type, AtomicReference<T> target) {
        this.type = type;
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 这里可以添加前置和后置处理逻辑
        // 例如权限检查、日志记录等
        if (target.get() == null) {
            throw new IllegalStateException("获取不到代理到目标对象");
        }
        try {

            return doInvoke(target.get(), method, args);
        } catch (Exception e) {

            log.error("执行异常:", e);
            throw new RuntimeException(e);
        }
    }

    public static <T> T doInvoke(Object obj, Method method, Object... args) throws InvocationTargetException,IllegalAccessException {
        return invokeRaw(obj, method, args);
    }

    /**
     * 执行方法
     *
     * <p>
     * 对于用户传入参数会做必要检查，包括：
     *
     * <pre>
     *     1、忽略多余的参数
     *     2、参数不够补齐默认值
     *     3、传入参数为null，但是目标参数类型为原始类型，做转换
     * </pre>
     *
     * @param <T>    返回对象类型
     * @param obj    对象，如果执行静态方法，此值为{@code null}
     * @param method 方法（对象方法或static方法都可）
     * @param args   参数对象
     * @return 结果
     * @throws InvocationTargetException 目标方法执行异常
     * @throws IllegalAccessException    访问异常
     */
    @SuppressWarnings("unchecked")
    public static <T> T invokeRaw(Object obj, Method method, Object... args) throws InvocationTargetException, IllegalAccessException {
        setAccessible(method);

        // 检查用户传入参数：
        // 1、忽略多余的参数
        // 2、参数不够补齐默认值
        // 3、通过NullWrapperBean传递的参数,会直接赋值null
        // 4、传入参数为null，但是目标参数类型为原始类型，做转换
        // 5、传入参数类型不对应，尝试转换类型
        final Class<?>[] parameterTypes = method.getParameterTypes();
        final Object[] actualArgs = new Object[parameterTypes.length];
        if (null != args) {
            for (int i = 0; i < actualArgs.length; i++) {
                if (i >= args.length || null == args[i]) {
                    // 越界或者空值
                    actualArgs[i] = null;
                }
                else if (false == parameterTypes[i].isAssignableFrom(args[i].getClass())) {
                    //对于类型不同的字段，尝试转换，转换失败则使用原对象类型
                    final Object targetValue =   args[i] ;
                    if (null != targetValue) {
                        actualArgs[i] = targetValue;
                    } else {
                        actualArgs[i] = args[i];
                    }
                } else {
                    actualArgs[i] = args[i];
                }
            }
        }


        return (T) method.invoke( obj, actualArgs);
    }

    public static <T extends AccessibleObject> T setAccessible(T accessibleObject) {
        if (null != accessibleObject && !accessibleObject.isAccessible()) {
            accessibleObject.setAccessible(true);
        }
        return accessibleObject;
    }

}